using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Threading;

namespace MiddlewareServer.Socket
{
    class SocketServer
    {
        private TcpListener _tcp;
        private Thread _thread;

        private int _port = 8000;

        private Dictionary<IPAddress, ClientObj> _clients = new Dictionary<IPAddress, ClientObj>();

        public SocketServer()
        {
        }

        public SocketServer(ReadDataDelegate readDelegate, ConnectDelegate connectDelegate, DisConnectDelegate disconnectDelegate)
        {
            _readDelegate += readDelegate;
            _connectDelegate += connectDelegate;
            _disConnectDelegate += disconnectDelegate;
        }

        public void Connect()
        {
            try
            {
                _thread = new Thread(new ThreadStart(Run));
                _thread.Start();

                //_thread.Join();
            }
            catch (Exception ex)
            {
            }
        }

        public void Run()
        {
            Console.WriteLine("[SS]::Run");

            TcpClient tcp;
            ClientObj client;
            IPAddress ip;

            IPAddress ipAddress = Dns.GetHostEntry("127.0.0.1"/*"localhost"*/).AddressList[0];
            _tcp = new TcpListener(ipAddress, _port);
            _tcp.Start();

            while (true)
            {
                try
                {
                    Console.WriteLine("[SS]::Run - waiting for client connection at port {0}...",_port);
                    tcp = _tcp.AcceptTcpClient();
                    ip = ((IPEndPoint)tcp.Client.RemoteEndPoint).Address;
                    client = new ClientObj(tcp, ReadData, DisConnectClient);
                    if (!_clients.ContainsKey(ip))
                    {
                        _clients.Add(ip, client);

                        if (_connectDelegate !=null)
                        {
                            //_connectDelegate(ip);
                        }
                        else
                        {
                            Console.Out.WriteLine("delegate is empty");
                        }

                        ThreadPool.QueueUserWorkItem(client.Run, this);
                    }
                }
                catch (Exception ex)
                {
                }

                Console.Out.WriteLine("[SS]::Run - client was added");
            }
        }

        public void WriteData(IPAddress ip, byte[] data)
        {
            try
            {
                if (_clients.ContainsKey(ip))
                    _clients[ip].Write(data);
                else
                    Console.Out.WriteLine("[SS]::WriteData - client doesn't exist");
            }
            catch (Exception ex)
            {
            }
        }

        public void ReadData(IPAddress ip, byte[] data)
        {
            if (_readDelegate != null)
                _readDelegate(ip, data);
        }

        public void ConnectClient(IPAddress ip)
        {
            if (_connectDelegate != null)
                _connectDelegate(ip);
        }

        public void DisConnectClient(IPAddress ip)
        {
            if (_disConnectDelegate != null)
                _disConnectDelegate(ip);
        }

        // delegates for communication betweend client and server
        #region delegates

        public delegate void ConnectDelegate(IPAddress ip);
        public delegate void DisConnectDelegate(IPAddress ip);
        public delegate void ReadDataDelegate(IPAddress ip, byte[] data);

        private ConnectDelegate _connectDelegate;
        private DisConnectDelegate _disConnectDelegate;
        private ReadDataDelegate _readDelegate;

        public ConnectDelegate ConnectEvent
        {
            set { _connectDelegate += value; }
        }

        public DisConnectDelegate DisConnectEvent
        {
            set { _disConnectDelegate += value; }
        }

        public ReadDataDelegate ReadDataEvent
        {
            set { _readDelegate += value; }
        }

        #endregion
    }
}
